package assign3.party;

/** The Bartender lives to serve students beer (of course)
 * 
 * Monitor Invariants:
 * -> drinkCount.length = lineup.length = numStudents  /\
 *    drinksInKeg <= kegSize /\
 *    if drinksInKeg <= 0 then student thread blocks (waits) /\
 *    if drinksInKeg > 0 then runner thread blocks (waits) /\
 *    if drinkCount[id] != min(drinkCount) then student thread blocks (waits)
 */
public class Assign3_Bartender
{
	private int[] drinkCount;
	private int drinksInKeg;
	private int kegSize;
	private boolean[] lineup;
	
	/*
	 * Constructor. The arguments are the number of glasses in a keg and the number of drinking students.
	 */
	public Assign3_Bartender(int kegSize, int numStudents)
	{
		drinkCount = new int[numStudents];
		lineup = new boolean[numStudents];
		for(int i=0; i<numStudents; ++i)
		{
			drinkCount[i] = 0;
			lineup[i] = false;
		}
		this.kegSize = kegSize;
		drinksInKeg = kegSize;
	}
	
	/*
	 * called by the students and blocks until the student's glass is filled, and
	 */
	synchronized public void getBeer(int id)
	{
		lineup[id] = true;
		while(true)
		{
			if(drinksInKeg <= 0)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e) { e.printStackTrace(); }
			}
			else if(!fairPickFromLine(id))
			{
				try
				{
					wait();
				}
				catch (InterruptedException e) { e.printStackTrace(); }
			}
			else
			{
				System.out.println("Student " + id + " Getting Beer...");
				drinksInKeg--;
				drinkCount[id]++;
				lineup[id] = false;
				notifyAll();
				break;
			}
		}
	}
	
	/*
	 * called by the runner and blocks until the old keg is consumed and the new keg is installed.
	 */
	synchronized public void installNewKeg()
	{
		while(drinksInKeg > 0)
		{
			try
			{
				wait();
			}
			catch (InterruptedException e) { e.printStackTrace(); }
		}
		System.out.println("Runner Installing Keg...");
		drinksInKeg = kegSize;
		notifyAll();
	}
	
	private boolean fairPickFromLine(int me)
	{	
		for(int i = 0; i < lineup.length; ++i)
		{
			if(lineup[i] == true) // if in lineup
			{
				if( drinkCount[i] < drinkCount[me])
				{
					return false;
				}
			}
		}
		return true;
	}
}
